﻿using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows.Input;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using GalaSoft.MvvmLight.Threading;
using unTroublemaker;

namespace unTroublemakerUI.ViewModel
{
    public class MainWindowViewModel : ViewModelBase, IMainWindowViewModel
    {
        private const string LastPathSettingName = "LastPath";

        private readonly ISpecificationBrowser _specBrowser;
        private readonly Settings _settings;
        private string _specificationPath;
        private Specification _specification;
        private string _errorMessage;
        private int _itemCount;
        private int _itemsProcessed;
        private bool _isRunning;
        private IEnumerable<ISpecificationItemViewModel> _specItems;

        public MainWindowViewModel() : this(new Settings(), new SpecificationBrowser()) { }

        public MainWindowViewModel(Settings settings, ISpecificationBrowser specificationBrowser)
        {
            _specBrowser = specificationBrowser;
            _settings = settings;

            SetSpecification(settings.Get(LastPathSettingName));

            RunCommand = new RelayCommand(Run, CanRun);
            BrowseCommand = new RelayCommand(Browse);

            CommandManager.InvalidateRequerySuggested();
        }

        private void Browse()
        {
            _specBrowser.Path = SpecificationPath;
            if (!_specBrowser.BrowseSpecification())
                return;

            SetSpecification(_specBrowser.Path);
        }

        private void SetSpecification(string path)
        {
            if (string.IsNullOrEmpty(path))
                return;

            if (!File.Exists(path))
            {
                ErrorMessage = "File does not exist: " + path;
                return;
            }

            SpecificationPath = path;
            _settings.Save(LastPathSettingName, SpecificationPath);

            _specification = new Specification(SpecificationPath);
            ItemCount = CountItems(_specification.Items);

            ErrorMessage = !_specification.Valid ? _specification.Error : null;

            SpecificationItems = _specification.Items.Select(specItem => new SpecificationItemViewModel(specItem)).ToArray();
        }

        private int CountItems(ISpecificationItem[] specification)
        {
            return specification.Length + specification.Sum(s => CountItems(s.ChildItems));
        }

        private void Run()
        {
            IsRunning = true;
            var worker = new BackgroundWorker();
            worker.DoWork += RunSpecification;
            worker.RunWorkerAsync();
            worker.RunWorkerCompleted += delegate
                {
                    DispatcherHelper.RunAsync(() =>
                        {
                            IsRunning = false;
                            CommandManager.InvalidateRequerySuggested();
                        });
                };
        }

        private void RunSpecification(object sender, DoWorkEventArgs e)
        {
            var tester = new SpecificationTester(_specification);
            tester.ItemStatusChanged += ItemStatusChanged;
            tester.Run();
            tester.ItemStatusChanged -= ItemStatusChanged;
        }

        private void ItemStatusChanged(ISpecificationItem sender, StatusChangedEventArgs args)
        {
            if (args.NewRunStatus != RunStatus.Running)
                DispatcherHelper.RunAsync(() => ItemsProcessed++);
        }

        private bool CanRun()
        {
            return File.Exists(SpecificationPath) && !IsRunning;
        }

        public IEnumerable<ISpecificationItemViewModel> SpecificationItems
        {
            get { return _specItems; }
            private set { Set(() => SpecificationItems, ref _specItems, value); }
        }

        public ICommand RunCommand { get; private set; }
        public ICommand BrowseCommand { get; private set; }

        public string SpecificationPath
        {
            get { return _specificationPath; }
            set { Set(() => SpecificationPath, ref _specificationPath, value); }
        }

        public bool IsRunning
        {
            get { return _isRunning; }
            set { Set(() => IsRunning, ref _isRunning, value); }
        }

        public string ErrorMessage
        {
            get { return _errorMessage; }
            set
            {
                if (Set(() => ErrorMessage, ref _errorMessage, value))
                    RaisePropertyChanged(() => HasError);
            }
        }

        public bool HasError { get { return !string.IsNullOrEmpty(ErrorMessage); } }

        public int ItemCount
        {
            get { return _itemCount; }
            set { Set(() => ItemCount, ref _itemCount, value); }
        }

        public int ItemsProcessed
        {
            get { return _itemsProcessed; }
            set { Set(() => ItemsProcessed, ref _itemsProcessed, value); }
        }
    }
}
